Глава
14
Разработка
пользовательских тегов
На сегодняшний
день в ColdFusion входит более семидесяти встроенных тегов, позволяющих создавать
полноценные Web-приложения. Также сообщество разработчиков, использующих ColdFusion,
пpeредлагает набор пользовательских тегов для свободного распространения, с
которыми можно ознакомиться и при необходимости скачать из Internet по адресу
http://devex.allaire.com/DeveIoper/Gallery/CustomTags.cfm.
Впрочем,
почему бы нам не научиться создавать собственные пользовательские теги, тем
более что это не требует громадных усилий. Пользовательский тег представляет
собой некий код с возможностью его многократного использования, и поведение
которого может зависеть от передаваемых атрибутов.
Применение
пользовательских тегов позволяет ускорить процесс разработки Web-приложений
и облегчить сопровождение будущего продукта.
Пользовательские
теги в ColdFusion могут быть двух видов:
Как уже было
отмечено, CF-теги создаются на основе CFML-страниц и их имена записываются с
приставкой CF_. Например, для вызова тега, размещенного на странице MyFirstTag.cfm,
необходимо использовать следующую конструкцию:
<CF_MyFirstTag>
Пользовательский
тег может также записываться с указанием закрывающего тега, например:
<CF_MyFirstTag>
<TABLE border="0"><tr>
<TD bgcolor="Green"><FONT
color="White">Hello!</FONT></TD> </TR></TABLE>
</CF_MyFirstTag>
Размещать
пользовательский тег следует либо там, где располагается страница, вызывающая
его, либо в специально предусмотренном для этой цели каталоге. По умолчанию
каталог для хранения пользовательских тегов находится на X:\Cfusion\CustomTags,
где X — устройство, на которое был предустановлен ColdFusion Server. Но это
не мешает изменять существующий путь или добавлять новые пути для размещения
и в дальнейшем обнаружения системой пользовательских тегов. На рис. 14.1 показана
страница администратора "ColdFusion Administrator" открытая в разделе
Server | Extensions | Custom Tag Paths (Сервер Расширения | Путь к пользовательским
тегам), где предоставляется возможность регистрировать новые пути для пользовательских
тегов.
Рис.
14.1. Страница "ColdFusion Administrator", раздел Custom
Tag Paths
При вызове
тега также можно передавать значения переменных в качестве атрибутов. Например,
предположим, что необходимо создать пользовательский
тег по преобразованию температуры воздуха из значений по шкале Цельсия (С) в
градусы по Фаренгейту (F) и наоборот. Предварительно для данного примера определим
синтаксис нового тега:
<CF_TemperatureTransform
Value="Number"
[CtoF="Boolean"]>
где Value
— числовое значение, требуемое для преобразования; ctoF — логическое значение,
указывающее направление преобразования. Причем атрибут value является обязательным
в отличие от атрибута ctoF. По умолчанию направление преобразования происходит
из (С) в (F) впрочем, так же, как и при указании атрибута равным I, Yes,
True или просто при упоминании данного атрибута без указания значения.
Теперь, когда
задача определена и сформулированы все условия, можно приступить к написанию
страницы TemperatureTransform.cfm (листинг 14.1).
Листинг
14.1. Код страницы TemperatureTransform.cfm
<!--- Изменение
температуры (Transformation of temperature) --->
<CFIF isDefined("Attributes.Value")>
<CFIF isDefined("Attributes.CtoF")>
<CFIF isNumeric(Attributes.Value) AND isBoolean(Attributes.CtoF)>
<CFIF YesNoFormat(Attributes.CtoF)
equal "Yes">
<CFSET Caller.Temperature = Attributes.Value*1.8+32 & " F">
<CFELSE>
<CFSET Caller.Temperature = (Attributes.Value-32)*5/9 & " C"> </CFIF>
<CFELSE>
<CFSET Caller.Temperature
= "Errorl"> </CFIF> <CFELSE>
<CFSET Caller.Temperature = Attributes.Value*1.8+32 & " F"> </CFIF>
<CFELSE>
<CFSET Caller.Temperature = "Error2">
</CFIF>
После чего
остается только использовать новый тег. Например, так:
<CF_TemperatureTransform Value = "21">
<CFOUTPUT>Temperature:
iVariables.Temperaturel</CFOUTPUT>
В результате
будет выведена следующая строка:
Temperature:
69.8 F
Если есть
необходимость преобразования в обратную сторону, можно применить наш тег следующим
образом:
<CF_TemperatureTransform Value = "69.8" CtoF="No">
<CFOUTPUT>Temperature:
#Variables.Temperaturel</CFOUTPUT>
Что позволит
получить следующий результат:
Temperature:
21 С
В приведенном
выше примере были использованы переменные атрибутов пользовательских тегов Attributes,
позволяющие анализировать передаваемые атрибуты в теле пользовательского тега,
и также применялись переменные источников вызова caller для передачи переменных
из шаблонов пользовательских тегов ColdFusion.
Также можно
указывать вложение тегов, т. е. когда в вызываемом шаблоне пользовательского
тега происходит обращение к другому пользовательскому тегу. Например, следующим
шагом мы несколько модернизируем наш пример преобразования температуры.
В нашем примере
при операции перевода значения температуры может возникнуть ситуация, когда
значение после запятой достигнет немыслимых размеров. Предположим, если перевести
по нашей формуле 70° по Фаренгейту в значение по шкале Цельсия, то в результате
получим:
Temperature:
21.1111111111 С
Что является
не очень удачным результатом. Для решения этой проблемы, в общем-то, достаточно
воспользоваться существующей в ColdFusion функцией NumberFormat () . Однако
мы "усложним" себе задачу и создадим еще один пользовательский тег
для форматирования числовых величин (листинг 14.2).
Синтаксис
пользовательского тега, позволяющего выполнить форматирование числовых величин,
таков:
<CF_NumFormat
Value = "Number"
Format = "Mask">
где Value
— числовое значение, требуемое для форматирования; Format -маска, определяющая
формат и соответствующая правилу задания маски для функции ColdFusion NumberFormat
() .
Листинг
14.2. Код страницы NumFormat.cfm
<CFIF isDefined("Attributes.Value") AND isDefined("Attributes.Format")>
<CFSET Caller.Num
= NumberFormat(Attributes.Value, Attributes.Format)>
<CFELSE>
<CFSET Caller.Num
= NumberFormat(0, "___._")>
</CFIF>
Теперь, когда
мы обладаем таким замечательным пользовательским тегом, как CF_NumFormat, его
можно использовать следующим образом:
<CF_NumFormat
Value = "10.123" Format = "__._">
<CFOUTPUT>#Variables.Num#</CFOUTPUT>
В результате
получим число:
10.1
Далее представим
обновленный код пользовательского тега для преобразования температуры (листинг
14.3), где используется вложение пользовательского тега <CF_NumFormat>.
Листинг
14.3. Код страницы Temperature!ransform2.cfm
<!--- Transformation
of temperature Version2 --->
<CFSET Caller.Temperature = "">
<CFIF isDefined("Attributes.Value")>
<CFIF isDefined("attributes.CtoF")>
<CFIF isNumeric(Attributes.Value) AND isBoolean(Attributes.CtoF)>
<CFIF YesNoFormat(Attributes.CtoF) equal "Yes">
<CFSET Val = Attributes.Value*1.8+32>
<CFSET Measurement
= " F"> <CFELSE>
<CFSET Val = (Attributes.Value-32)*5/9>
<CFSET Measurement = " C"> </CFIF>
<CFELSE>
<CFSET Caller.Temperature = "Error1"> </CFIF>
<CFELSE>
<CFSET Val = Attributes.Value*1.8+32>
<CFSET Measurement = " F"> </CFIF>
<CFELSE>
<CFSET Caller.Temperature
= "Error2"> </CFIF>
<CFIF Caller.Temperature
contains "Error">
<CFELSE>
<CF_NumFormat
Value = "#Val#" Format = "___._" >
<CFSET Caller.Temperature
= #Variables.Numl & "#Measurement#">
</CFIF>
Возвращаясь
к нашей проблеме, переведем все те же 70° по Фаренгейту в значение по шкале
Цельсия, как показано ниже:
<CF_TemperatureTransform2 Value = "70" CtoF = "No">
<CFOUTPUT>Temperature:
#Variables.Temperaturel</CFOUTPUT>
В результате
получим такой результат:
Temperature:
21.1 С
При использовании
вложенных тегов применяются функции, позволяющие получать доступ к наследственным
данным:
К примеру,
если тег <CF_NumFormat> дополнить строкой Кода:
<CFOUTPUT>#GetBaseTagList()#</CFOUTPUT>
то при прямом
обращении результатом будет являться значение, представленное ниже:
CFOUTPUT,CFIF,CF_NUMFORMAT
При обращении
к тегу <CF_NumFormat> внутри пользовательского тега <CF_TemperatureTransform2>
как к вложенному тегу, результатом будет значение:
CFOUTPUT,CFIF,CF_NUMFORMAT,
CFELSE,CF_TEMPERATURETRANSFORM2
Обработав
полученное значение, можно "вычленить" всех имеющихся "родителей"
данного тега.
Как уже было
отмечено в одном из первых примеров, пользовательский тег может записываться
с указанием закрывающего тега, при этом ColdFusion позволяет отслеживать начало
и окончание выполнения пользовательского тега. Определить же способ выполнения
тега можно посредством переменной ExecutionMode, используя при этом относительную
ссылку ThisTag, как показано в следующем примере:
<CFSWITCH EXPRESSION=#ThisTag.ExecutionModet>
<CFCASE VALUE
= 'Start'> <!---Начало тега--->
</CFCASE>
<CFCASE VALUE
= 'End'>
<!---Конец
тега--->
</CFCASE>
</CFSWITCH>
Здесь блок,
условно назовем его start, выполняется в начале тега, соответственно блок End
выполняется при указании окончания тега.
Для демонстрации
применения пользовательских тегов с указанием окончания приведем пример тега,
позволяющего преобразовывать текст в табличную форму с возможностью изменения
характеристик данной таблицы и осуществлять исправление некоторых грамматических
ошибок, допущенных в тексте. Назовем этот тег <CF_MyTable>.
Синтаксис тега <CF_муТаblе>:
<CF_MyTable
Width = "Number"
Border = "Number"
CellPadding
= "Number"
Cellspacing
= "Number"
TBGColor = "Color"
TColor = "Color"
TSize = "Number"
TText = "String">
Текст... </CF_MyTable>
где:
Далее представим
код пользовательского тега <CF_MyTabie> (листинг 14.4).
Листинг
14,4. Код страницы MyTable.cfm
<!----My
Table---->
<CFSWITCH EXPRESSION=tThisTag.ExecutionMode#>
<CFCASE VALUE
= 'Start'>
<!--- Начало
тега (Start tag processing) --->
<CFOUTPUT>
<TABLE width="#Attributes.Width!" border="#Attributes.Border#"
cellpadding="#Attributes.CellPadding!"
cell3pacing="#Attributes.Cellspacing!"
bgcolor="!Attributes.TBGcolor!">
<TR><TD
align="center">
<FONT color="#Attributes.TColor#"
size="!Attributes.TSize!">
#Attributes.TText! </FONT> </TD></TR>
</TABLE>
</CFOUTPUT> </CFCASE> <CFCASE VALUE = 'End'>
<!----Начало
тега---->
<CFOUTPUT>
<TABLE width
= '^Attributes.Width!"
border = "!Attributes.Border!" cellpadding =
"#Attributes.CellPadding!" cellspacing =
"#Attributes.CellSpacing!">
<TR><TD bgcolor = "#Attributes .TBGcolor!"></TD></TR>
<TR><TD
align = "right">
Time: !TimeFormat(Now(), "HH:mm:ss")! </TD></TR>
</TABLE>
<CFSET ThisTag.GeneratedContent
=
"<table
width = " & Attributes.Width! &
" border
= " & Attributes.Border! &
" cellpadding
= " & #Attributes.CellPadding# &
" cellspacing
= " & !Attributes.Cellspacing! &
"><tr><td>"
&
#REReplaceNoCase(thisTag.GeneratedContent,
"(w|tt)he","the","ALL")#
&
"</td></tr></table>"> </CFOUTPUT>
</CFCASE>
</CFSWITCH>
В отличие
от тега <CF_TemperatureTransform2>, рассмотренного ранее, мы не анализируем
наличие необходимых атрибутов и удовлетворение определенных правил при указании
значений этих атрибутов для пользовательского тега <CF_MyTable>, т. к.
целью данного примера является демонстрация определения способа выполнения тега,
и при желании данный тег всегда можно совершенствовать и совершенствовать. Обратите
лишь ваше внимание на использование переменной GeneratedContent. В сочетании
с относительной ссылкой ThisTag данная переменная позволяет определять содержание
между началом и окончанием тега. Функция же ColdFusion REReplaceNoCase(), также
используемая в приведенном теге, в данном контексте позволяет заменять словосочетания
'whe' и 'tthe' на 'the' по всему тексту.
В качестве
примера применения пользовательского тега <сг_муТаblе> создадим файл следующего
содержания:
<HTML>
<HEAD>
<TITLE>Custom
Tag</TITLE> </HEAD> <BODY>
<CF_MyTable Width = "380" Border = "0"
TBGColor = "Green"
CellPadding = "1" Cellspacing = "1"
TColor = "White" TSize = "4" TText =
"Grand Central
Depot">
Grand Central Depot the predecessor of vhe
present terminal, was whe first station opened
in 1871 by vhe "Commodore". </CF_MyTable>
</BODY>
</HTML>
Здесь преднамеренно
по тексту были допущены грамматические ошибки, обнаруживаемые тегом <CF_MyTable>.
Впрочем,
для передачи значений атрибутов пользовательского тега можно также использовать
зарезервированный атрибут AttributeCoilection, где в качестве значения необходимо
указывать заранее созданную структуру (structure). При этом ключи структуры
воспринимаются как атрибуты пользовательского
тега, а значения ключей структуры играют роль значений этих атрибутов. К Примеру:
<CFSET AttribMyTable
= StructNew()>
<CFSET AttribMyTable.Width
= "380">
<CFSET AttribMyTable.Border
= "0">
<CFSET AttribMyTable.TBGColor
= "Green">
<CFSET AttribMyTable.CellPadding
= "1">
<CFSET AttribMyTable.Cellspacing
= "1">
<CFSET AttribMyTable.TColor
= "White">
<CFSET AttribMyTable.TSize
= "4">
<CFSET AttribMyTable.TText
= "Grand Central Depot">
<CF_MyTable
AttributeCollection = #AttribMyTable#>
Grand Central
Depot the predecessor of tthe present terminal, was whe first station opened
in 1871 by tthe "Commodore".
</CF MyTable>
Зарезервированный
атрибут AttributeCollection можно применять в сочетании с пользовательскими
атрибутами, например:
<CF_MyTable
AttributeCollection = #AttribMyTable# NewAttribute = "1">
Более подробно
с понятием структур можно познакомиться в главе 13.
ColdFusion
предоставляет еще один вариант для включения пользовательских тегов, уже с добавлением
тега <CFMODULE>, где можно указывать полный путь размещения файла-шаблона.
Синтаксис:
<CFMODULE
TEMPLATE = "template" NAME = "tag_name"
ATTRIBUTECOLLECTION = "collection_structure"
ATTRIBUTE_NAME1
= "value" ATTRIBUTE_NAME2 = "value"
В табл. 14.1
приведено описание атрибутов тега <CFMODULE>.
Таблица
14.1. Описание атрибутов тега <CFMODULE>
Атрибут |
Описание |
||
TEMPLATE NAME ATTRIBUTECOLLECTION ATTRIBUTE_NAME |
Шаблон. Используется
вместо наименования. Определяет путь к CFML-файлу относительно текущей
страницы Наименование
пользовательского тега. Применяется вместо шаблона в определенной'
форме. Например, если шаблон тега MyTable.cfm размещается в каталоге
CustomTags\MyTags, тогда наименование пользовательского тега следует
указать как MyTags . MyTable Структура. Ключи
структуры воспринимаются как атрибуты пользовательского тега, а значения
ключей структуры играют роль значений передаваемых атрибутов. Необязательный
атрибут Атрибуты для
пользовательского тега. Можно использовать необходимое количество
атрибутов для определения нужных параметров для пользовательского
тега. Необязательный атрибут |
||
Проведем
пример использования тега <CFMODULE> с применением указания шаблона:
<CFMODULE
TEMPLATE = "MyTable.cfm"
ATTRIBUTECOLLECTION
= #AttribMyTable#>
Grand Central
Depot the predecessor of tthe present terminal,
was whe first
station opened in 1871 by tthe "Commodore".
</CFMODULE>
В начале
главы мы уже отметили, что CFX-теги разрабатываются на основе Java или C++.
И если вас не удовлетворяет в полной мере язык CFML, то можно воспользоваться
одним из упомянутых языков программирования для реализации пользовательских
тегов, позволяющих расширить возможности собственного Web-приложения.
Давайте остановимся
на Java, являющемся объектно-ориентированным языком программирования. Чтобы
запустить программу на Java, необходимо использовать
интерпретатор Java, который будет выполнять инструкции откомпилированного байт-кода.
Поскольку байт-код Java является архитектурно-нейтральным, программы на Java
могут работать только на тех платформах, где реализована виртуальная машина
Java (JVM, Java Virtual Machine), являющаяся интерпретатором и средством динамической
поддержки.
На рис. 14.2
показана страница администратора "ColdFusion Administrator", открытая
в разделе Server | Extensions | JVM and Java Settings (Сервер | Расширения
| Параметры JVM и Java), где при использовании пользовательских тегов, разработанных
на Java, необходимо указать месторасположение виртуальной машины в поле Java
Virtual Machine Path (Путь к виртуальной машине Java).
Рис.
14.2. Страница "ColdFusion Administrator", раздел JVM
and Java Settings
А также на
странице администратора JVM and Java Settings (Параметры JVM и Java)
устанавливаются:
Из числа
устанавливаемых параметров на этой же странице, находящихся вне зоны видимости,
наиболее интересными для нас являются:
При формировании
Java-кода, на основе которого предполагается создать CFX-тег, необходимо с использованием
оператора import включать пакет com. allaire.cfx,", расположенный в архиве
cfx.jar. К примеру, создадим CFX-тег на основе Java, где объединяются значения
трех входящих атрибутов А, в и с, а результат в виде строки передается на страницу,
вызывающую пользовательский тег. Код данного примера приведен в листинге 14.5.
Листинг
14.5. Java-код MyTag.java
import com.allaire.cfx.*
;
public class
MyTag implements CustomTag (
public void
processRequest(Request request, Response response) throws Exception
{
String A = request.getAttribute("A");
String В = request.getAttribute("В");
String С = request.getAttribute("C");
String ABC =
toABC(A, B, C) ;
response.write( "Result: " + ABC ); }
private String
toABC(String a, String b, String c){
String abc =
a + b + c;
return abc;
}
Следующим шагом нам необходимо откомпилировать исходный текст Java из файла MyTag.java в байт-код Java-файла MyTag.class. Для подобных целей в Java существует программа javac.
Еще раз продемонстрируем
команду для нашего примера:
javac -classpath
cfx.jar MyTag.java
где параметр
командной строки -classpath определяет путь, используемый javac для поиска классов,
на которые имеются ссылки в исходном тексте. В нашем случае еще раз повторимся,
это архив cfx.jar.
При отсутствии
ошибок в исходном тексте в результате компиляции будет создан файл MyTag.class,
который следует поместить по одному из указанных путей в поле Class Path
(Путь к файлам классов) или в поле Dynamic Class Load Path (Путь
к динамически загружаемым классам) раздела Server | Extensions | JVM and
Java Settings (Сервер | Расширения | Параметры JVM и Java), размещенном
на странице администратора. Например, на D:\CFusion\CFX или D:\CFusion\CFX\Dinamic.
Размещая Java-классы в каталоге по одному из путей, указанных в поле Class
Path (Путь к файлам классов), мы обрекаем их на статичное поведение. То
есть при модификации откомпилированного Java-класса результат его выполнения
будет неизменен до тех пор, пока ColdFusion Application Server не будет перезагружен.
Впрочем, если существует необходимость использовать Java-класс как динамический,
то его следует размещать по адресу, указанному в поле Dynamic Class Load
Path (Путь к динамически загружаемым классам), что позволяет не перезагружать
службу ColdFusion для отображения очередных изменений, произведенных по отношению
к выполняемому Java-классу.
Заключительным
действием для удачной работы тега CFX следует зарегистрировать созданный тег,
используя все ту же страницу администратора ColdFusion, либо воспользовавшись
системным реестром.
Для регистрации
на странице администратора "ColdFusion Administrator" следует перейти
в раздел Server | Extensions | CFX Tags (Сервер | Расширения | Теги CFX),
нажать кнопку Register Java CFX (Регистрация
Java CFX)
или кнопку Register C++ CFX (Регистрация C++ CFX) в зависимости
от используемого языка программирования и заполнить предоставленную форму. На
рис. 14.3 показана форма регистрации Java CFX с данными для приведенного примера.
Рис.
14.3. Форма регистрации Java CFX
Здесь:
После заполнения
формы для подтверждения следует нажать одну из кнопок Submit Changes (Отправить
изменения) — для удобства работы на странице расположены две кнопки Submit
Changes (Отправить изменения) в верхней и в нижней части окна. На рис. 14.4
показан результат регистрации.
Рис.
14.4. Страница "ColdFusion Administrator", раздел CFX
Tags после регистрации
Также для
регистрации CFX-тегов можно использовать системный реестр. Например, для регистрации
тега <CFX_MyTag> необходимо создать сценарий в следующей форме:
REGEDIT4
[HKEY_LOCAL_MACHINE\SOFTWARE\Allaire\
ColdFusion\CurrentVersion\CustomTags
\CFX_MyTag]
"Description"=""
"Type"="java"
"JavaClass"="MyTag"
Затем приведенный
сценарий следует сохранить в файле с расширением reg, что в последующем позволит
зарегистрировать указанный CFX-тег посредством выполнения этого файла или с
помощью команды импорта (Import Registry File (Импорт файла реестра)),
запустив программу Regedit (Редактор реестра). Впрочем, с таким же успехом зарегистрировать
CFX-тег можно вручную, открыв окно редактора реестра.
И только
теперь, когда CFX-тег успешно создан и зарегистрирован, можно приступить к
демонстрации примера вызова этого тега. Далее представим пример, позволяющий
использовать разработанный нами тег <CFX_MуTаg> на основе MyTag.class:
<HTML>
<HEAD>
<TITLE>CFX_MyTag</TITLE> </HEAD>
<BODY>
<CFX_MyTag A = "Aa" В = "Bb" С = "Cc">
</BODY>
</HTML>
Результатом
выполнения приведенного примера будет вывод следующей строки:
Result: АаВЬСс
Вернемся
к нашему примеру MyTag.java (см. листинг 14.5). В нем ключевыми действиями является
использование встроенных объектов Request и Response. Объект Request служит
для получения значений запроса, посланного со стороны страницы. К примеру, следующий
код определяет строковую переменную А и присваивает ей значение передаваемого
атрибута с аналогичным именем, используя метод GetAttribute:
String A = request.getAttribute("A")
;
Объект Response
имеет обратное назначение и предназначен для передачи информации вызываемой
странице. Например, представленный ниже код отображает на странице клиента значение,
определяемое с помощью метода write.
response.write("Result:
" + ABC);
В свою очередь встроенный объект Request имеет следующие методы:
Встроенный
объект Response также имеет набор методов:
Стоит отметить,
что помимо объектов Request и Response существует еще один встроенный объект
— Query, обеспечивающий интерфейс для работы с запросами ColdFusion. Мы же ограничимся
только перечислением методов для этого объекта:
Мы не рассматриваем
в этой главе примеры создания CFX-тегов с помощью языка C++. Только отметим,
что при использовании программного продукта Microsoft Visual C++ в среде Windows
вы можете запускать так называемые мастера для создания CFX-тегов (ColdFusion
Tag Wizard). При этом должен быть установлен комплект CFXAPI Tag Development
Kit. Мастер генерирует DLL-файлы с основной структурой тега, содержащего единственную
процедуру. Изменяя и тестируя этот тег, вы сможете быстро научиться работать
в среде API.
Подведем итог. В данной главе вы познакомились с CFML-тегом <CFMODULE> и CFML-функцией REReplaceNoCase.